﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Win32;
using System.Windows.Forms;
using System.Configuration;
using System.Net;

namespace cmdPhone
{
    /// <summary>
    /// Helps setup app.config and download/create the required pfx/certs
    /// </summary>
    static class SetupWizzard
    {
        /// <summary>
        /// Run Wizzard wrapper
        /// </summary>
        public static void web21cSetupWizzard()
        {
            Interaction.output(Resources.setup_welcome_message);
            Interaction.input();

            bool finished = runWizzard();

            if (finished == true)
            {
                Interaction.output(Resources.say_setup_completed_ok);
            }
            else
            {
                Interaction.output(Resources.say_setup_not_finished);
            }
        }


        /// <summary>
        /// Performs orchestration between the various methods in the class for the wizzard
        /// </summary>
        /// <returns></returns>
        private static bool runWizzard()
        {
            bool continueWizzard = true;

            continueWizzard = promptUserForSDKAccount();
            if (continueWizzard == false) return false;

            Config config = new Config();

            continueWizzard = promptUserForEnvironmentType(ref config);
            if (continueWizzard == false) return false;

            continueWizzard = promptUserForPfxFile(ref config);
            if (continueWizzard == false) return false;

            continueWizzard = selectPfxFile(ref config);
            if (continueWizzard == false) return false;

            continueWizzard = promptUserForBTCertFile(ref config);
            if (continueWizzard == false) return false;

            wizzardPasswordStep(ref config);

            loadConfigValues(config);

            Interaction.output(Resources.setup_finished_ok);

            return true;

        }


        /// <summary>
        /// Asks user whether he/she has an account at sdk.bt.com
        /// If user doesn't then will open browser on the registration page.
        /// </summary>
        /// <returns>true if user has account false otherwise</returns>
        private static bool promptUserForSDKAccount() 
        {
            Interaction.output(Resources.prompt_have_sdk_account);
            Interaction.output(Resources.prompt_yes_or_no);
            string userInput = Interaction.input();
            if (userInput.ToLower() == "y" || userInput.ToLower() == "yes")
            {
                return true;
            }
            else if (userInput.ToLower() == "n" || userInput.ToLower() == "no")
            {
                Interaction.output(Resources.say_go_to_sdk_website);
                Interaction.output(Resources.say_need_account_for_cert);
                System.Diagnostics.Process.Start(Resources.create_sdk_account_url);
                return false;
            }
            else
            {
                Interaction.output(Resources.invalid_option);
                return false;
            }

            return true;

        }


        /// <summary>
        /// Asks user whether the required environment is sandbox or production
        /// </summary>
        /// <param name="config">Config instance by reference</param>
        /// <returns>false if user provides invalid input</returns>
        private static bool promptUserForEnvironmentType(ref Config config)
        {
            Interaction.output(Resources.prompt_for_certificate_type);
            Interaction.outputNoSpace(Resources.choose_certificate_type_by_typing_key);

            string userInput = Interaction.input();
            if (userInput.ToLower() == "s" || userInput.ToLower() == "sandbox")
            {
                config.Web21c_Environment = "sandbox";
            }
            else if (userInput.ToLower() == "p" || userInput.ToLower() == "production")
            {
                config.Web21c_Environment = "production";
            }
            else
            {
                Interaction.output(Resources.invalid_option);
                return false;
            }

            return true;

        }


        /// <summary>
        /// Asks user if he/she has PFX cert, if user doesn't have one will call createPfxCertificate
        /// </summary>
        /// <param name="config">Instance of Config by reference</param>
        /// <returns>true if user has pfx file (or BT pfx tool was lauched) false otherwise</returns>
        private static bool promptUserForPfxFile(ref Config config)
        {
            Interaction.output(String.Format(Resources.prompt_have_pfx_cert, config.Web21c_Environment));
            Interaction.outputNoSpace(Resources.prompt_yes_or_no);

            string userInput = Interaction.inputNoSpace();
            if (userInput.ToLower() == "n")
            {
                bool continueWizzard = createPfxCertificate(config.Web21c_Environment);
                if (continueWizzard == false) return false;

                Interaction.output(Resources.prompt_notify_when_pfx_created);
                Interaction.inputNoSpace();
                return true;

            }
            else if (userInput.ToLower() == "y")
            {
                return true;
            }
            else
            {
                return false;
            }
        }


        /// <summary>
        /// Prompts user to select the PFX file
        /// </summary>
        /// <param name="config">Instance of Config by reference</param>
        /// <returns>true if file was selected false otherwise</returns>
        private static bool selectPfxFile(ref Config config)
        {
            Interaction.output(Resources.prompt_select_pfx_file);
            string certFile = selectFileDialog(Resources.filter_choose_pfx_certificate);
            if (String.IsNullOrEmpty(certFile))
            {
                return false;
            }
            else
            {
                config.certFile = certFile;
                return true;
            }

        }


        /// <summary>
        /// Launches the web21c PFX creation Java tool
        /// Checks that system has JRE installed, will prompt user to download JRE if not present
        /// </summary>
        /// <param name="pfxType">production or sandbox</param>
        /// <returns>true if pfx tool was launched, false if JRE not present in the system</returns>
        private static bool createPfxCertificate(string pfxType)
        {
            if (isJavaInstalled())
            {
                Interaction.output(Resources.java_Pfx_tool_instrucitons);
                Interaction.inputNoSpace();

                System.Diagnostics.Process.Start(Resources.java_pfx_tool_uri);

                return true;
            }
            else
            {
                Interaction.output(Resources.notify_need_jre_for_pfx_tool);
                Interaction.outputNoSpace(Resources.notify_jre_download_uri);
                return false;
            }
        }


        /// <summary>
        /// Asks user if he/she has the relevant BT Cert file and will act accordingly
        /// (download said file or prompt user to select file)
        /// </summary>
        /// <param name="config">Instance of Config by reference</param>
        /// <returns>true if file selected/created false otherwise</returns>
        private static bool promptUserForBTCertFile(ref Config config)
        {

            if (config.Web21c_Environment == "sandbox")
            {
                Interaction.output(Resources.prompt_have_bt_sandbox_cert);
            }
            else //production
            {
                Interaction.output(Resources.prompt_have_bt_production_cert);
            }

            Interaction.outputNoSpace(Resources.prompt_yes_or_no);
            string userInput = Interaction.inputNoSpace();

            if (userInput.ToLower() == "n")
            {
                bool continueWizzard = downloadBTCertFile(ref config);
                if (continueWizzard == false) return false;
                return true;

            }
            else if (userInput.ToLower() == "y")
            {
                bool continueWizzard = selectBTCertFile(ref config);
                if (continueWizzard == false) return false;
                return true;
            }
            else
            {
                Interaction.output(Resources.invalid_option);
                return false;
            }
        }


        /// <summary>
        /// Download relevant BT cert file
        /// </summary>
        /// <param name="config">Instance of Config by reference</param>
        /// <returns>false if there was a problem downloading the file, true otherwise</returns>
        private static bool downloadBTCertFile(ref Config config)
        {
            try
            {
                string path = System.IO.Path.GetDirectoryName(config.certFile);
                path = path + "\\";
                WebClient client = new WebClient();
                if (config.Web21c_Environment == "sandbox")
                {
                    string file = Resources.bt_sandbox_cert.Insert(0, path);
                    Interaction.output(String.Format(Resources.notify_downloading_bt_cert, Resources.bt_sandbox_cert));
                    client.DownloadFile(Resources.bt_sandbox_cert_uri, file);
                    config.serverCertFile = file;
                }
                else //production
                {
                    string file = Resources.bt_production_cert.Insert(0, path);
                    Interaction.output(String.Format(Resources.notify_downloading_bt_cert, Resources.bt_production_cert));
                    client.DownloadFile(Resources.bt_production_cert_uri, file);
                    config.serverCertFile = file;
                }
                return true;

            }
            catch (Exception ex)
            {
                Interaction.dealWithException(ex);
                return false;
            }

        }


        /// <summary>
        /// prompts user to select the relevant BT cert file
        /// </summary>
        /// <param name="config">Instance of config by reference</param>
        /// <returns>true if file selected, false otherwise</returns>
        private static bool selectBTCertFile(ref Config config)
        {
            string serverCertFile;

            if (config.Web21c_Environment == "sandbox")
            {
                serverCertFile = selectFileDialog(Resources.filter_bt_cert_sandbox);
            }
            else //production
            {
                serverCertFile = selectFileDialog(Resources.filter_bt_cert_production);
            }

            if (String.IsNullOrEmpty(serverCertFile))
            {
                return false;
            }

            config.serverCertFile = serverCertFile;

            return true;
        }


        /// <summary>
        /// Prompts user to type the PFX password
        /// </summary>
        /// <param name="config">Instance of Config by reference</param>
        private static void wizzardPasswordStep(ref Config config)
        {
            Interaction.output(Resources.prompt_pfx_password);
            Interaction.outputNoSpace(Resources.prompt_pfx_password_comment);
            config.certPassword = Interaction.input();
        }


        /// <summary>
        /// Loads Config instance into app.config
        /// </summary>
        /// <param name="config">instance of Config class</param>
        private static void loadConfigValues(Config config)
        {
            Configuration appConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

            configSetValue(appConfig, "Web21c_Environment", config.Web21c_Environment);
            configSetValue(appConfig, "certFile", config.certFile);
            configSetValue(appConfig, "serverCertFile", config.serverCertFile);
            configSetValue(appConfig, "certPassword", config.certPassword);

            // Save the configuration file.
            appConfig.Save(ConfigurationSaveMode.Modified);

            // Force a reload of a changed section
            ConfigurationManager.RefreshSection("appSettings");
            
        }


        /// <summary>
        /// Sets key value into appConfig
        /// </summary>
        /// <param name="appConfig">System.Configuration.Configuration instance</param>
        /// <param name="key">config key</param>
        /// <param name="value">key value</param>
        public static void configSetValue(Configuration appConfig, string key, string value)
        {

            if (appConfig.AppSettings.Settings[key] != null)
            {

                appConfig.AppSettings.Settings.Remove(key);

            }

            appConfig.AppSettings.Settings.Add(key, value);

        }


        /// <summary>
        /// Helper dialog box to select a file in the system
        /// </summary>
        /// <param name="dialogFilter">dialog filter, e.g "*.config|*.config"</param>
        /// <returns>filename with full path, null if user canceled or closed the dialog box</returns>
        private static string selectFileDialog(string dialogFilter)
        {
            OpenFileDialog fileSelectDialog = new OpenFileDialog();

            try
            {
                fileSelectDialog.Filter = dialogFilter;
                fileSelectDialog.ShowDialog();

                //If user clicked on "cancel" or closed the dialog box
                if (String.IsNullOrEmpty(fileSelectDialog.FileName))
                {
                    return null;
                }

                return fileSelectDialog.FileName;
            }
            finally
            {
                fileSelectDialog.Dispose();
            }

        }


        /// <summary>
        /// Asserts whether Java is installed on the system
        /// </summary>
        /// <returns>true/false</returns>
        private static bool isJavaInstalled()
        {
            RegistryKey RK = Registry.LocalMachine.OpenSubKey(Resources.java_registry_key);

            if (RK != null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}
